// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

#region

using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using System.Security.Cryptography;

#endregion

namespace Neto.Encry;

public class SM3
{
    /// <summary>
    ///     生成指定位数的SM3密钥。
    /// </summary>
    /// <param name="keySizeBit">密钥位数（默认为512位）。</param>
    /// <returns>生成的密钥字节数组。</returns>
    /// <exception cref="ArgumentException">如果密钥位数不是8的倍数或小于等于0。</exception>
    public static byte[] GenerateKeyToBytes(int keySizeBit = 512)
    {
        if (keySizeBit <= 0 || keySizeBit % 8 != 0)
            throw new ArgumentException("Key size must be a positive multiple of 8.", nameof(keySizeBit));

        var keySize = keySizeBit / 8;
        var keyRandom = RandomNumberGenerator.GetBytes(keySize);
        return keyRandom;
    }

    /// <summary>
    ///     生成指定位数的SM3密钥。
    /// </summary>
    /// <param name="keySizeBit">密钥位数（默认为512位）。</param>
    /// <returns>生成的密钥字节数组。</returns>
    /// <exception cref="ArgumentException">如果密钥位数不是8的倍数或小于等于0。</exception>
    public static string GenerateKey(int keySizeBit)
    {
        var key = GenerateKeyToBytes(keySizeBit);
        return Convert.ToBase64String(key);
    }

    /// <summary>
    ///     使用SM3算法对数据进行哈希。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <returns>哈希后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据为空。</exception>
    public static byte[] Hash(byte[] data)
    {
        if (data == null) throw new ArgumentNullException(nameof(data));

        SM3Digest sm3 = new();
        sm3.BlockUpdate(data, 0, data.Length);
        var md = new byte[sm3.GetDigestSize()];
        sm3.DoFinal(md, 0);
        return md;
    }

    /// <summary>
    ///     使用SM3算法对数据进行哈希。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <returns>哈希后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据为空。</exception>
    public static string ToHashBase64(string data)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data));
        return Convert.ToBase64String(hashByte);
    }

    /// <summary>
    ///     使用SM3算法对数据进行哈希，并转换为base32字符串。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <returns></returns>
    public static string ToHashBase32(string data)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data));
        //转换为base32字符串
        return hashByte.ToBase32String();
    }

    /// <summary>
    ///     使用SM3算法对数据进行哈希，并转换为16进制字符串。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <returns></returns>
    public static string ToHashHex(string data)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data));
        //转换为16进制字符串
        return hashByte.ToHexString();
    }


    /// <summary>
    ///     使用SM3算法对数据进行带密钥的哈希（HMAC）。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <param name="key">密钥。</param>
    /// <returns>HMAC后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据或密钥为空。</exception>
    public static byte[] Hash(byte[] data, byte[] key)
    {
        if (data == null) throw new ArgumentNullException(nameof(data));
        if (key == null) throw new ArgumentNullException(nameof(key));

        SM3Digest sm3 = new();
        HMac mac = new(sm3);
        KeyParameter keyParameter = new(key);
        mac.Init(keyParameter);
        mac.BlockUpdate(data, 0, data.Length);
        var result = new byte[mac.GetMacSize()];
        mac.DoFinal(result, 0);
        return result;
    }

    /// <summary>
    ///     使用SM3算法对数据进行带密钥的哈希（HMAC）。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <param name="key">密钥。</param>
    /// <returns>HMAC后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据或密钥为空。</exception>
    public static string ToHashBase64(string data, string key)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(key));
        return Convert.ToBase64String(hashByte);
    }

    /// <summary>
    ///     使用SM3算法对数据进行带密钥的哈希（HMAC），并转换为32进制字符串。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <param name="key">密钥。</param>
    /// <returns>HMAC后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据或密钥为空。</exception>
    public static string ToHashBase32(string data, string key)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(key));
        return hashByte.ToBase32String();
    }

    /// <summary>
    ///     使用SM3算法对数据进行带密钥的哈希（HMAC），并转换为16进制字符串。
    /// </summary>
    /// <param name="data">要哈希的数据。</param>
    /// <param name="key">密钥。</param>
    /// <returns>HMAC后的字节数组。</returns>
    /// <exception cref="ArgumentNullException">如果数据或密钥为空。</exception>
    public static string ToHashHex(string data, string key)
    {
        var hashByte = Hash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(key));
        return hashByte.ToHexString();
    }
}